import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import time
import hashlib
import threading
import configparser

class FileSplitterMerger:
    def __init__(self, root):
        self.root = root
        self.root.title("文件分割合并工具 v1.0")
        self.root.geometry("800x500")
        
        # 初始化变量
        self.cancel_flag = False
        self.progress_var = tk.DoubleVar()
        self.status_var = tk.StringVar(value="就绪")
        self.speed_var = tk.StringVar(value="速度: -")
        self.time_left_var = tk.StringVar(value="剩余时间: -")
        
        # 配置默认值
        self.split_size_var = tk.StringVar(value="10")
        self.split_unit_var = tk.StringVar(value="MB")
        self.output_dir_var = tk.StringVar()
        self.merge_filename_var = tk.StringVar()
        
        self.setup_ui()
        
    def setup_ui(self):
        # 顶部标题
        title_frame = tk.Frame(self.root)
        title_frame.pack(pady=10)
        tk.Label(title_frame, text="文件分割合并工具", font=("Arial", 16, "bold")).pack()
        
        # 主内容区域
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
        
        # 左侧分割功能区
        split_frame = tk.LabelFrame(main_frame, text="文件分割")
        split_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10)
        
        # 文件选择
        file_frame = tk.Frame(split_frame)
        file_frame.pack(fill=tk.X, pady=5)
        tk.Button(file_frame, text="选择文件", command=self.select_file).pack(side=tk.LEFT)
        self.file_path_var = tk.StringVar()
        tk.Entry(file_frame, textvariable=self.file_path_var, state='readonly', width=40).pack(side=tk.LEFT, padx=5)
        
        # 文件信息
        self.file_info_var = tk.StringVar()
        tk.Label(split_frame, textvariable=self.file_info_var, anchor='w').pack(fill=tk.X, pady=2)
        
        # 输出目录
        output_frame = tk.Frame(split_frame)
        output_frame.pack(fill=tk.X, pady=5)
        tk.Label(output_frame, text="输出目录:").pack(side=tk.LEFT)
        tk.Entry(output_frame, textvariable=self.output_dir_var, width=30).pack(side=tk.LEFT, padx=5)
        tk.Button(output_frame, text="浏览", command=self.select_output_dir).pack(side=tk.LEFT)
        
        # 分割大小设置
        size_frame = tk.Frame(split_frame)
        size_frame.pack(fill=tk.X, pady=5)
        tk.Label(size_frame, text="分割大小:").pack(side=tk.LEFT)
        tk.Entry(size_frame, textvariable=self.split_size_var, width=10).pack(side=tk.LEFT, padx=5)
        unit_options = ["KB", "MB", "GB"]
        ttk.Combobox(size_frame, textvariable=self.split_unit_var, values=unit_options, width=5, state="readonly").pack(side=tk.LEFT)
        
        # 分割按钮
        tk.Button(split_frame, text="开始分割", command=self.start_split, bg="#4a7abc", fg="white").pack(pady=10)
        
        # 右侧合并功能区
        merge_frame = tk.LabelFrame(main_frame, text="文件合并")
        merge_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10)
        
        # 目录选择
        dir_frame = tk.Frame(merge_frame)
        dir_frame.pack(fill=tk.X, pady=5)
        tk.Button(dir_frame, text="选择目录", command=self.select_merge_dir).pack(side=tk.LEFT)
        self.merge_dir_var = tk.StringVar()
        tk.Entry(dir_frame, textvariable=self.merge_dir_var, state='readonly', width=40).pack(side=tk.LEFT, padx=5)
        
        # 合并文件信息
        self.merge_info_var = tk.StringVar()
        tk.Label(merge_frame, textvariable=self.merge_info_var, anchor='w').pack(fill=tk.X, pady=2)
        
        # 合并文件名
        filename_frame = tk.Frame(merge_frame)
        filename_frame.pack(fill=tk.X, pady=5)
        tk.Label(filename_frame, text="合并文件名:").pack(side=tk.LEFT)
        tk.Entry(filename_frame, textvariable=self.merge_filename_var, width=30).pack(side=tk.LEFT, padx=5)
        
        # 合并按钮
        tk.Button(merge_frame, text="开始合并", command=self.start_merge, bg="#5a9e5a", fg="white").pack(pady=10)
        
        # 底部状态区域
        status_frame = tk.Frame(self.root)
        status_frame.pack(fill=tk.X, padx=20, pady=10)
        
        # 进度条
        progress_frame = tk.Frame(status_frame)
        progress_frame.pack(fill=tk.X, pady=5)
        ttk.Progressbar(progress_frame, variable=self.progress_var, maximum=100).pack(fill=tk.X)
        
        # 状态信息
        info_frame = tk.Frame(status_frame)
        info_frame.pack(fill=tk.X)
        tk.Label(info_frame, textvariable=self.status_var, anchor='w').pack(side=tk.LEFT, fill=tk.X, expand=True)
        tk.Label(info_frame, textvariable=self.speed_var).pack(side=tk.LEFT, padx=10)
        tk.Label(info_frame, textvariable=self.time_left_var).pack(side=tk.LEFT)
        
        # 取消按钮
        self.cancel_btn = tk.Button(status_frame, text="取消", command=self.cancel_operation, state=tk.DISABLED)
        self.cancel_btn.pack(pady=5)
    
    def select_file(self):
        file_path = filedialog.askopenfilename()
        if file_path:
            self.file_path_var.set(file_path)
            file_size = os.path.getsize(file_path)
            self.file_info_var.set(f"文件名: {os.path.basename(file_path)}, 大小: {self.format_size(file_size)}")
            # 设置默认输出目录
            dir_name = os.path.splitext(os.path.basename(file_path))[0]
            self.output_dir_var.set(os.path.join(os.path.dirname(file_path), dir_name))
    
    def select_output_dir(self):
        dir_path = filedialog.askdirectory()
        if dir_path:
            self.output_dir_var.set(dir_path)
    
    def select_merge_dir(self):
        dir_path = filedialog.askdirectory()
        if dir_path:
            self.merge_dir_var.set(dir_path)
            # 尝试自动检测配置文件
            config_path = os.path.join(dir_path, "split_config.ini")
            if os.path.exists(config_path):
                config = configparser.ConfigParser()
                config.read(config_path)
                if 'SplitInfo' in config:
                    original_name = config['SplitInfo'].get('OriginalName', '')
                    self.merge_filename_var.set(original_name)
                    part_count = config['SplitInfo'].get('PartCount', '0')
                    self.merge_info_var.set(f"检测到分割文件: {part_count}个.part文件")
    
    def start_split(self):
        file_path = self.file_path_var.get()
        if not file_path or not os.path.exists(file_path):
            messagebox.showerror("错误", "请选择有效的文件")
            return
        
        output_dir = self.output_dir_var.get()
        if not output_dir:
            messagebox.showerror("错误", "请输入输出目录")
            return
        
        try:
            split_size = float(self.split_size_var.get())
            unit = self.split_unit_var.get()
            # 转换为字节
            if unit == "KB": split_size *= 1024
            elif unit == "MB": split_size *= 1024 * 1024
            elif unit == "GB": split_size *= 1024 * 1024 * 1024
        except ValueError:
            messagebox.showerror("错误", "请输入有效的分割大小")
            return
        
        if split_size <= 0:
            messagebox.showerror("错误", "分割大小必须大于0")
            return
        
        # 创建输出目录
        os.makedirs(output_dir, exist_ok=True)
        
        # 启动分割线程
        self.cancel_flag = False
        self.cancel_btn.config(state=tk.NORMAL)
        threading.Thread(target=self.split_file, args=(file_path, output_dir, split_size), daemon=True).start()
    
    def start_merge(self):
        merge_dir = self.merge_dir_var.get()
        if not merge_dir or not os.path.exists(merge_dir):
            messagebox.showerror("错误", "请选择有效的目录")
            return
        
        output_filename = self.merge_filename_var.get()
        if not output_filename:
            messagebox.showerror("错误", "请输入合并后的文件名")
            return
        
        # 获取所有.part文件
        part_files = sorted([f for f in os.listdir(merge_dir) if f.endswith('.part')])
        if not part_files:
            messagebox.showerror("错误", "目录中没有找到.part文件")
            return
        
        # 检查配置文件
        config_path = os.path.join(merge_dir, "split_config.ini")
        if not os.path.exists(config_path):
            if not messagebox.askyesno("警告", "未找到配置文件，继续合并吗？"):
                return
        
        # 启动合并线程
        self.cancel_flag = False
        self.cancel_btn.config(state=tk.NORMAL)
        threading.Thread(target=self.merge_files, args=(merge_dir, part_files, output_filename), daemon=True).start()
    
    def split_file(self, file_path, output_dir, chunk_size):
        try:
            chunk_size = int(chunk_size)
            file_size = os.path.getsize(file_path)
            total_chunks = (file_size + chunk_size - 1) // chunk_size
            base_name = os.path.splitext(os.path.basename(file_path))[0]
            
            # 创建配置文件
            config = configparser.ConfigParser()
            config['SplitInfo'] = {
                'OriginalName': os.path.basename(file_path),
                'PartCount': str(total_chunks),
                'ChunkSize': str(chunk_size),
                'SplitTime': time.strftime("%Y-%m-%d %H:%M:%S"),
                'OriginalHash': self.calculate_file_hash(file_path)
            }
            with open(os.path.join(output_dir, 'split_config.ini'), 'w') as configfile:
                config.write(configfile)
            
            # 开始分割
            start_time = time.time()
            bytes_written = 0
            chunk_num = 1
            
            with open(file_path, 'rb') as f:
                while bytes_written < file_size:
                    if self.cancel_flag:
                        self.update_status("分割已取消")
                        return
                    
                    chunk = f.read(chunk_size)
                    if not chunk:
                        break
                    
                    part_name = f"{base_name}{chunk_num}.part"
                    part_path = os.path.join(output_dir, part_name)
                    
                    with open(part_path, 'wb') as part_file:
                        part_file.write(chunk)
                    
                    bytes_written += len(chunk)
                    chunk_num += 1
                    
                    # 更新进度
                    progress = (bytes_written / file_size) * 100
                    self.progress_var.set(progress)
                    
                    # 计算速度和剩余时间
                    elapsed = time.time() - start_time
                    speed = bytes_written / (elapsed + 1e-6)  # 避免除以0
                    remaining_bytes = file_size - bytes_written
                    time_left = remaining_bytes / speed if speed > 0 else 0
                    
                    self.status_var.set(f"分割中: {chunk_num-1}/{total_chunks} 文件")
                    self.speed_var.set(f"速度: {self.format_size(speed)}/s")
                    self.time_left_var.set(f"剩余时间: {self.format_time(time_left)}")
            
            # 完成分割
            self.progress_var.set(100)
            self.status_var.set(f"分割完成! 共生成 {chunk_num-1} 个文件")
            messagebox.showinfo("完成", f"文件分割完成!\n共生成 {chunk_num-1} 个文件\n保存位置: {output_dir}")
        
        except Exception as e:
            messagebox.showerror("错误", f"分割过程中发生错误:\n{str(e)}")
        finally:
            self.cancel_btn.config(state=tk.DISABLED)
    
    def merge_files(self, merge_dir, part_files, output_filename):
        try:
            output_path = os.path.join(merge_dir, output_filename)
            
            # 检查文件是否已存在
            if os.path.exists(output_path):
                if not messagebox.askyesno("确认", "文件已存在，是否覆盖？"):
                    return
            
            total_size = sum(os.path.getsize(os.path.join(merge_dir, f)) for f in part_files)
            merged_size = 0
            start_time = time.time()
            
            with open(output_path, 'wb') as output_file:
                for i, part_file in enumerate(part_files):
                    if self.cancel_flag:
                        self.update_status("合并已取消")
                        return
                    
                    part_path = os.path.join(merge_dir, part_file)
                    with open(part_path, 'rb') as f:
                        while True:
                            chunk = f.read(1024 * 1024)  # 每次读取1MB
                            if not chunk:
                                break
                            output_file.write(chunk)
                            merged_size += len(chunk)
                            
                            # 更新进度
                            progress = (merged_size / total_size) * 100
                            self.progress_var.set(progress)
                            
                            # 计算速度和剩余时间
                            elapsed = time.time() - start_time
                            speed = merged_size / (elapsed + 1e-6)
                            remaining_bytes = total_size - merged_size
                            time_left = remaining_bytes / speed if speed > 0 else 0
                            
                            self.status_var.set(f"合并中: {i+1}/{len(part_files)} 文件")
                            self.speed_var.set(f"速度: {self.format_size(speed)}/s")
                            self.time_left_var.set(f"剩余时间: {self.format_time(time_left)}")
            
            # 验证完整性
            config_path = os.path.join(merge_dir, "split_config.ini")
            if os.path.exists(config_path):
                config = configparser.ConfigParser()
                config.read(config_path)
                original_hash = config['SplitInfo'].get('OriginalHash', '')
                current_hash = self.calculate_file_hash(output_path)
                
                if original_hash and original_hash == current_hash:
                    self.status_var.set(f"合并完成! 文件校验成功")
                    messagebox.showinfo("完成", f"文件合并完成!\n保存位置: {output_path}\n文件校验成功")
                else:
                    self.status_var.set(f"合并完成! 但文件校验失败")
                    messagebox.showwarning("警告", f"文件合并完成!\n但文件校验失败，可能文件已损坏")
            else:
                self.status_var.set(f"合并完成! (未校验)")
                messagebox.showinfo("完成", f"文件合并完成!\n保存位置: {output_path}\n(未进行完整性校验)")
        
        except Exception as e:
            messagebox.showerror("错误", f"合并过程中发生错误:\n{str(e)}")
        finally:
            self.cancel_btn.config(state=tk.DISABLED)
    
    def calculate_file_hash(self, file_path, block_size=65536):
        """计算文件的SHA256哈希值"""
        sha256 = hashlib.sha256()
        with open(file_path, 'rb') as f:
            while True:
                data = f.read(block_size)
                if not data:
                    break
                sha256.update(data)
        return sha256.hexdigest()
    
    def cancel_operation(self):
        self.cancel_flag = True
        self.status_var.set("正在取消操作...")
        self.cancel_btn.config(state=tk.DISABLED)
    
    def update_status(self, message):
        self.status_var.set(message)
        self.speed_var.set("速度: -")
        self.time_left_var.set("剩余时间: -")
    
    @staticmethod
    def format_size(size_bytes):
        """格式化文件大小"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size_bytes < 1024.0:
                return f"{size_bytes:.2f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.2f} TB"
    
    @staticmethod
    def format_time(seconds):
        """格式化时间"""
        if seconds < 60:
            return f"{seconds:.0f}秒"
        elif seconds < 3600:
            return f"{seconds // 60:.0f}分{seconds % 60:.0f}秒"
        else:
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            return f"{hours:.0f}时{minutes:.0f}分"

if __name__ == "__main__":
    root = tk.Tk()
    app = FileSplitterMerger(root)
    root.mainloop()